package com.qushiyun.devops.admin.sync.data.controller;


import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.qushiyun.devops.admin.core.util.SqlParserTool;
import com.qushiyun.devops.admin.core.util.SqlType;
import com.qushiyun.devops.admin.sync.data.mapper.TableLogMapper;
import com.qushiyun.devops.admin.sync.data.mapstrcut.TriggerJobConverter;
import com.qushiyun.devops.admin.sync.data.po.TableLog;
import com.qushiyun.devops.admin.sync.data.po.TriggerJob;
import com.qushiyun.devops.admin.sync.data.service.TriggerJobService;
import com.qushiyun.devops.admin.sync.data.vo.TriggerJobSelectVO;
import com.qushiyun.devops.admin.sync.data.vo.TriggerJobVO;
import com.qushiyun.devops.admin.sync.job.SyncTemplate;
import com.qushiyun.devops.admin.sync.schedule.JobScheduleService;
import com.qushiyun.devops.common.core.exception.BaseException;
import com.qushiyun.devops.common.core.exception.ParamException;
import com.qushiyun.devops.common.core.web.response.ResponseResult;
import com.qushiyun.devops.common.framework.mybatis.page.vo.PageVO;
import com.qushiyun.devops.common.log.annotation.Log;
import com.qushiyun.devops.common.log.enums.BusinessType;
import io.swagger.annotations.ApiOperation;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import javax.validation.Valid;
import lombok.AllArgsConstructor;
import net.sf.jsqlparser.JSQLParserException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 同步任务
 *
 * @author zhouzx
 */
@AllArgsConstructor
@RestController
@RequestMapping("/sync/job")
public class TriggerJobController {

    private final TriggerJobService triggerJobService;
    private final SyncTemplate syncTask;
    private final TableLogMapper tableLogMapper;
    private final JobScheduleService jobScheduleService;


    @GetMapping("/page")
    @PreAuthorize("@auth.hasUrl()")
    public ResponseResult<PageVO<TriggerJobVO>> page(PageDTO pageDTO, TriggerJob job) {
        PageVO<TriggerJobVO> pageVO = triggerJobService.pageList(pageDTO, job);
        return ResponseResult.success(pageVO);
    }

    @GetMapping("/tableLog/page")
    @PreAuthorize("@auth.hasUrl()")
    public ResponseResult<PageVO<TableLog>> tableLogPage(PageDTO pageDTO, TableLog log) {
        IPage<TableLog> pageResult = tableLogMapper.selectPage(pageDTO,
            Wrappers.lambdaQuery(log)
                .orderByDesc(TableLog::getBeginTime)
                .orderByDesc(TableLog::getDataNum)
        );
        PageVO res = new PageVO<>();
        res.setTotal(pageResult.getTotal());
        res.setList(pageResult.getRecords());
        return ResponseResult.success(res);
    }

    @GetMapping("/list")
    @PreAuthorize("@auth.hasUrl()")
    public ResponseResult<List<TriggerJobSelectVO>> list() {
        Wrapper<TriggerJob> wrapper = Wrappers.lambdaQuery(TriggerJob.class).select(TriggerJob::getId, TriggerJob::getName);
        List<TriggerJob> list = triggerJobService.list(wrapper);
        List<TriggerJobSelectVO> result = TriggerJobConverter.INSTANCE.convert(list);
        return ResponseResult.success(result);
    }

    @GetMapping("/getById/{id}")
    @PreAuthorize("@auth.hasUrl()")
    public ResponseResult<TriggerJob> getById(@PathVariable Long id) {
        return ResponseResult.success(triggerJobService.getById(id));
    }

    @PostMapping
    @PreAuthorize("@auth.hasUrl()")
    @Log(moduleName = "同步条件", businessType = BusinessType.ADD)
    public ResponseResult add(@Valid @RequestBody TriggerJob job) throws JSQLParserException {
        conv(job);
        validate(job);
        job.setCreateTime(new Date());
        job.setStartIndex(0);
        job.setStatus(0);
        job.setTriggerTime(null);
        job.setTableNum(0);
        job.setDataNum(0L);
        job.setCostTime(0L);
        triggerJobService.save(job);
        addToSchedule(job);
        return ResponseResult.success();
    }

    @PutMapping
    @ApiOperation("修改")
    @PreAuthorize("@auth.hasUrl()")
    @Log(moduleName = "同步条件", businessType = BusinessType.UPDATE)
    public ResponseResult update(@Valid @RequestBody TriggerJob job) throws JSQLParserException {
        conv(job);
        validate(job);
        triggerJobService.updateById(job);
        addToSchedule(job);
        return ResponseResult.success();
    }

    @DeleteMapping("/delete/{ids}")
    @ApiOperation("删除")
    @PreAuthorize("@auth.hasUrl()")
    @Log(moduleName = "同步条件", businessType = BusinessType.DELETE)
    public ResponseResult delete(@PathVariable Long[] ids) {
        triggerJobService.removeByIds(Arrays.asList(ids));
        return ResponseResult.success();
    }

    @PostMapping("run/{id}")
    public ResponseResult run(@PathVariable("id") long id) throws Exception {
        syncTask.execute(id);
        return ResponseResult.success();
    }

    private void conv(TriggerJob job) {
        if (job.getTriggerType() != null && job.getTriggerType() == 2) {
            job.setTriggerTimestamp(job.getTriggerTime().getTime());
            if (job.getTriggerTimestamp() < System.currentTimeMillis()) {
                throw new BaseException(500, "触发时间小于当前时间！");
            }

        }
    }

    private void addToSchedule(TriggerJob job) {
        if (job.getTriggerType() != null && job.getTriggerType() == 2) {
            TriggerJob schedule = new TriggerJob();
            schedule.setId(job.getId());
            schedule.setTriggerTimestamp(job.getTriggerTimestamp());
            schedule.setTriggerType(job.getTriggerType());
            jobScheduleService.offer(schedule);
        }
    }

    private void validate(TriggerJob job) throws JSQLParserException {
        String fromDbBeforeSql = job.getFromDbBeforeSql();
        List<String> sqls;
        if (StrUtil.isNotBlank(fromDbBeforeSql) && !fromDbBeforeSql.toLowerCase().contains("function")) {
            sqls = Arrays.asList(fromDbBeforeSql.split(";"));
            judeType(sqls);
        }
        String toDbBeforeSql = job.getToDbBeforeSql();
        if (StrUtil.isNotBlank(toDbBeforeSql) && !toDbBeforeSql.toLowerCase().contains("function")) {
            sqls = Arrays.asList(toDbBeforeSql.split(";"));
            judeType(sqls);
        }

        String fromDbAfterSql = job.getFromDbAfterSql();
        sqls = Arrays.asList(fromDbAfterSql.split(";"));
        if (StrUtil.isNotBlank(fromDbAfterSql) && !fromDbAfterSql.toLowerCase().contains("function")) {
            judeType(sqls);
        }

        String toDbAfterSql = job.getToDbAfterSql();
        if (StrUtil.isNotBlank(toDbAfterSql) && !toDbAfterSql.toLowerCase().contains("function")) {
            sqls = Arrays.asList(toDbAfterSql.split(";"));
            judeType(sqls);
        }

        String fromDbErrorSql = job.getFromDbErrorSql();
        if (StrUtil.isNotBlank(fromDbErrorSql) && !fromDbErrorSql.toLowerCase().contains("function")) {
            sqls = Arrays.asList(fromDbErrorSql.split(";"));
            judeType(sqls);
        }

        String toDbErrorSql = job.getToDbErrorSql();
        if (StrUtil.isNotBlank(toDbErrorSql) && !toDbErrorSql.toLowerCase().contains("function")) {
            sqls = Arrays.asList(toDbErrorSql.split(";"));
            judeType(sqls);
        }
    }

    private static void judeType(List<String> sqls) throws JSQLParserException {
        for (String sql : sqls) {
            if (SqlParserTool.getSqlType(sql) == SqlType.SELECT) {
                throw new ParamException("不允许有查询语句！");
            }
        }
    }

}